package com.example.test_event_and_callback.infras.async;
import com.example.test_event_and_callback.infras.async.ano.AsyncDataDemander;
import com.example.test_event_and_callback.infras.async.ano.AsyncDataProvider;
import com.example.test_event_and_callback.infras.async.ano.Demander;
import org.reflections.Reflections;
import org.reflections.scanners.Scanners;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ThreadPoolExecutor;


public class AsyncExecuteProxy {

    private AsyncExecutor<?> executor;

    private void setExecutor(AsyncExecutor<?> executor) {
        this.executor = executor;
    }

    public <T> T execute(T demanderObj,String s){
        return executor.execute(demanderObj,s);
    }

    public static Builder newBuilder(){
        return new Builder();
    }

    public static class Builder{

        private String[] demanderScanPaths = {"com.example.test_event_and_callback.impl"};
        private String[] providerScanPaths = {"com.example.test_event_and_callback.impl"};
        private MethodCallType methodCallType;
        private ThreadPoolExecutor taskPool;

        public Builder setTaskPool(ThreadPoolExecutor pool) {
            taskPool = pool;
            return this;
        }

        public Builder setMethodCallType(MethodCallType type) {
            methodCallType = type;
            return this;
        }

        public AsyncExecuteProxy build(){
            AsyncExecuteProxy asyncExecuteProxy = new AsyncExecuteProxy();
            AsyncExecutor<MethodCallType> asyncExecutor = new AsyncExecutor<>();
            asyncExecutor.setTaskPool(taskPool);
            buildDemanderProcessorMap(asyncExecutor);
            buildProviderProcessor(asyncExecutor);
            asyncExecuteProxy.setExecutor(asyncExecutor);
            return asyncExecuteProxy;
        }

        private void buildDemanderProcessorMap(AsyncExecutor<?> asyncExecutor){
            Map<Class<?>, AsyncDemanderProcessor<?>> map = new HashMap<>();
            //扫描配置的每个路径
            for (String prefix:demanderScanPaths) {
                Reflections reflections = new Reflections(prefix);
                Set<Class<?>> annotatedClasses = reflections.getTypesAnnotatedWith(AsyncDataDemander.class);
                //路径下的每个类
                for (Class<?> annotatedClass:annotatedClasses) {
                    MethodCallType methodCallType = annotatedClass.getAnnotation(AsyncDataDemander.class).value();
                    map.put(annotatedClass,AsyncDemanderProcessor.getInstance(annotatedClass, methodCallType));
                }
            }
            asyncExecutor.setDemanderProcessorMap(map);
        }

        private void buildProviderProcessor(AsyncExecutor<?> asyncExecutor){
//            Map<Class<?>,AsyncDemanderProcessor<?>> demanderProcessorMap = asyncExecutor.getDemanderProcessorMap();
            Map<Class<?>, Map<String,Method>> methodMap = new HashMap<>();
            Reflections totalReflections = null;
            for (String prefix : providerScanPaths) {
                ConfigurationBuilder configurationBuilder = new ConfigurationBuilder()
                        .addUrls(ClasspathHelper.forPackage(prefix))
                        .addScanners(Scanners.MethodsAnnotated);
                Reflections reflections = new Reflections(configurationBuilder);
                totalReflections = totalReflections == null ? reflections : totalReflections.merge(reflections);
//                totalReflections = reflections.merge(totalReflections);
            }
            if (totalReflections == null) {
                throw new RuntimeException("providerScanPaths路径为空");
            }
            Set<Method> annotatedMethodSet = totalReflections.getMethodsAnnotatedWith(AsyncDataProvider.class);
            for (Method annotatedMethod:annotatedMethodSet) {
                Demander[] demanders = annotatedMethod.getAnnotation(AsyncDataProvider.class).value();
                for (Demander demanderLocation:demanders) {
                    Class<?> demander = demanderLocation.demander();
                    methodMap.computeIfAbsent(demander,d -> new HashMap<>())
                            .put(demanderLocation.param(),annotatedMethod);
                }
            }
            asyncExecutor.getDemanderProcessorMap().forEach((key,value) -> {
                AsyncProviderProcessor<?> providerProcessor = AsyncProviderProcessor.getInstance(key, methodCallType);
                providerProcessor.loadProviderMethodMap(methodMap.get(key));
                value.setProviderProcessor(providerProcessor);
            });
        }
    }

}
